import CryptoJS from 'crypto-js'

/**
 * 工具类，包含各种实用的方法和功能
 */
const tool = {
	// 本地存储相关操作
	data: {
		/**
		 * 设置本地存储项
		 * @param key 存储项的键
		 * @param data 要存储的数据
		 * @param datetime 可选的过期时间（默认为 0）
		 * @returns 存储操作的结果
		 */
		set(key: string, data: any, datetime: number = 0) {
			const cacheValue = {
				content: data,
				datetime: datetime === 0 ? 0 : Date.now() + datetime * 1000
			}
			return localStorage.setItem(key, JSON.stringify(cacheValue))
		},
		/**
		 * 获取本地存储项
		 * @param key 要获取的存储项的键
		 * @returns 存储项的内容或 null（如果不存在或已过期）
		 */
		get(key: string) {
			try {
				const value = JSON.parse(localStorage.getItem(key)!)
				if (value) {
					let nowTime = Date.now()
					if (nowTime > value.datetime && value.datetime != 0) {
						localStorage.removeItem(key)
						return null
					}
					return value.content
				}
			} catch (err) {
				return null
			}
		},
		/**
		 * 更新缓存时间
		 * @param key 存储项的键
		 * @param datetime 可选的过期时间（默认为 0）
		 * @returns 更新操作的结果
		 */
		time(key: string, datetime: number = 0) {
			const cachedData = JSON.parse(localStorage.getItem(key)!)
			if (cachedData) {
				cachedData.datetime = datetime === 0 ? 0 : Date.now() + datetime * 1000
				localStorage.setItem(key, JSON.stringify(cachedData))
				return true
			}
			return false
		},
		/**
		 * 移除本地存储项
		 * @param key 要移除的存储项的键
		 * @returns 移除操作的结果
		 */
		remove(key: string) {
			return localStorage.removeItem(key)
		},
		/**
		 * 清空本地存储
		 * @returns 清空操作的结果
		 */
		clear() {
			return localStorage.clear()
		}
	},
	// 会话存储相关操作
	session: {
		/**
		 * 设置会话存储项
		 * @param table 存储项的键
		 * @param settings 要存储的设置数据
		 * @returns 存储操作的结果
		 */
		set(table: string, settings: any) {
			var _set = JSON.stringify(settings)
			return sessionStorage.setItem(table, _set)
		},
		/**
		 * 获取会话存储项
		 * @param table 要获取的存储项的键
		 * @returns 存储项的内容或 null（如果不存在）
		 */
		get(table: string) {
			var data = sessionStorage.getItem(table)
			try {
				data = JSON.parse(data!)
			} catch (err) {
				return null
			}
			return data
		},
		/**
		 * 移除会话存储项
		 * @param key 要移除的存储项的键
		 * @returns 移除操作的结果
		 */
		remove(key: string) {
			return localStorage.removeItem(key)
		},
		/**
		 * 清空会话存储
		 * @returns 清空操作的结果
		 */
		clear() {
			return localStorage.clear()
		}
	},
	// Cookie 相关操作
	cookie: {
		/**
		 * 设置 Cookie
		 * @param name Cookie 的名称
		 * @param value Cookie 的值
		 * @param config Cookie 的配置选项
		 */
		set(
			name: string,
			value: any,
			config: {
				expires?: number | null
				path?: string | null
				domain?: string | null
				secure?: boolean
				httpOnly?: boolean
			}
		) {
			var cfg = {
				expires: null,
				path: null,
				domain: null,
				secure: false,
				httpOnly: false,
				...config
			}
			var cookieStr = `${name}=${encodeURI(value)}`
			if (cfg.expires) {
				var exp = new Date()
				exp.setTime(exp.getTime() + parseInt(String(cfg.expires)) * 1000)
				cookieStr += `;expires=${exp.toUTCString()}`
			}
			if (cfg.path) {
				cookieStr += `;path=${cfg.path}`
			}
			if (cfg.domain) {
				cookieStr += `;domain=${cfg.domain}`
			}
			document.cookie = cookieStr
		},
		/**
		 * 获取指定名称的 Cookie 值
		 * @param name 要获取的 Cookie 名称
		 * @returns Cookie 的值或 null（如果不存在）
		 */
		get(name: string) {
			var arr = document.cookie.match(new RegExp('(^| )' + name + '=([^;]*)(;|$)'))
			if (arr != null) {
				return unescape(arr[2])
			} else {
				return null
			}
		},
		/**
		 * 移除指定名称的 Cookie
		 * @param name 要移除的 Cookie 名称
		 */
		remove(name: string) {
			var exp = new Date()
			exp.setTime(exp.getTime() - 1)
			document.cookie = `${name}=;expires=${exp.toUTCString()}`
		}
	},
	/**
	 * 屏幕全屏相关操作
	 * @param element 要全屏操作的元素
	 */
	screen(element: HTMLElement) {
		var isFull = !!((document as any).webkitIsFullScreen || (document as any).mozFullScreen || (document as any).msFullscreenElement || document.fullscreenElement)
		if (isFull) {
			if (document.exitFullscreen) {
				document.exitFullscreen()
			} else if ((document as any).msExitFullscreen) {
				;(document as any).msExitFullscreen()
			} else if ((document as any).mozCancelFullScreen) {
				;(document as any).mozCancelFullScreen()
			} else if ((document as any).webkitExitFullscreen) {
				;(document as any).webkitExitFullscreen()
			}
		} else {
			if (element.requestFullscreen) {
				element.requestFullscreen()
			} else if ((element as any).msRequestFullscreen) {
				;(element as any).msRequestFullscreen()
			} else if ((element as any).mozRequestFullScreen) {
				;(element as any).mozRequestFullScreen()
			} else if ((element as any).webkitRequestFullscreen) {
				;(element as any).webkitRequestFullscreen()
			}
		}
	},
	/**
	 * 深度复制对象
	 * @param obj 要复制的对象
	 * @returns 复制后的新对象
	 */
	objCopy(obj: any) {
		return JSON.parse(JSON.stringify(obj))
	},
	/**
	 * 日期格式化
	 * @param date 要格式化的日期
	 * @param fmt 格式化模板（默认为 'yyyy-MM-dd hh:mm:ss'）
	 * @returns 格式化后的日期字符串
	 */
	dateFormat(date: number | Date | string, fmt: string = 'yyyy-MM-dd hh:mm:ss') {
		date = new Date(date)
		const o: Record<string, number> = {
			'M+': date.getMonth() + 1, // 月份
			'd+': date.getDate(), // 日
			'h+': date.getHours(), // 小时
			'm+': date.getMinutes(), // 分
			's+': date.getSeconds(), // 秒
			'q+': Math.floor((date.getMonth() + 3) / 3), // 季度
			S: date.getMilliseconds() // 毫秒
		}
		if (/ (y+) /.test(fmt)) {
			fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length))
		}
		for (var k in o) {
			if (new RegExp('(' + k + ')').test(fmt)) {
				fmt = fmt.replace(RegExp.$1, RegExp.$1.length === 1 ? String(o[k]) : ('00' + String(o[k])).substr(String(o[k]).length))
			}
		}
		return fmt
	},
	/**
	 * 数字添加千分位分隔符
	 * @param num 要处理的数字
	 * @returns 添加分隔符后的数字字符串
	 */
	groupSeparator(num: string) {
		num = num + ''
		if (!num.includes('.')) {
			num += '.'
		}
		return num
			.replace(/(\d)(?=(\d{3})+\.)/g, function (_$0, $1) {
				return $1 + ','
			})
			.replace(/\.$/, '')
	},
	// 加密相关操作
	crypto: {
		/**
		 * 计算数据的 MD5 哈希值
		 * @param data 要计算哈希值的数据
		 * @returns MD5 哈希值字符串
		 */
		MD5(data: string) {
			return CryptoJS.MD5(data).toString()
		},
		BASE64: {
			/**
			 * 对数据进行 Base64 加密
			 * @param data 要加密的数据
			 * @returns 加密后的 Base64 字符串
			 */
			encrypt(data: string) {
				return CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(data))
			},
			/**
			 * 对 Base64 字符串进行解密
			 * @param cipher 要解密的 Base64 字符串
			 * @returns 解密后的字符串
			 */
			decrypt(cipher: string) {
				return CryptoJS.enc.Base64.parse(cipher).toString(CryptoJS.enc.Utf8)
			}
		},
		AES: {
			/**
			 * 使用 AES 加密数据
			 * @param data 要加密的数据
			 * @param secretKey 加密密钥
			 * @param config 加密配置（可选）
			 * @returns 加密后的字符串
			 */
			encrypt(
				data: string,
				secretKey: string,
				config: {
					iv?: string
					mode?: 'ECB' | 'CBC' | 'CFB' | 'OFB'
					padding?: 'Pkcs7' | 'ZeroPadding' | 'NoPadding'
				} = {}
			) {
				if (secretKey.length % 8 != 0) {
					secretKey = secretKey.padEnd(Math.ceil(secretKey.length / 8) * 8, '0')
				}
				const result = CryptoJS.AES.encrypt(data, CryptoJS.enc.Utf8.parse(secretKey), {
					iv: CryptoJS.enc.Utf8.parse(config.iv || ''),
					mode: CryptoJS.mode[config.mode || 'ECB'],
					padding: CryptoJS.pad[config.padding || 'Pkcs7']
				})
				return result.toString()
			},
			/**
			 * 使用 AES 解密数据
			 * @param cipher 要解密的密文
			 * @param secretKey 解密密钥
			 * @param config 解密配置（可选）
			 * @returns 解密后的字符串
			 */
			decrypt(
				cipher: string,
				secretKey: string,
				config: {
					iv?: string
					mode?: 'ECB' | 'CBC' | 'CFB' | 'OFB'
					padding?: 'Pkcs7' | 'ZeroPadding' | 'NoPadding'
				} = {}
			) {
				const result = CryptoJS.AES.decrypt(cipher, CryptoJS.enc.Utf8.parse(secretKey), {
					iv: CryptoJS.enc.Utf8.parse(config.iv || ''),
					mode: CryptoJS.mode[config.mode || 'ECB'],
					padding: CryptoJS.pad[config.padding || 'Pkcs7']
				})
				return CryptoJS.enc.Utf8.stringify(result)
			}
		}
	},
	/**
	 * 生成 API 签名
	 * @param params API 参数
	 * @param salt 盐值
	 * @returns 带有签名的参数对象
	 */
	generateApiSignature(params: any, salt: string) {
		const _params = { ...params, salt }
		const keys = Object.keys(_params)
			.filter((key) => {
				const value = _params[key]
				return value !== '' && value !== null
			})
			.sort()

		const values = keys.reduce((acc: string[], key) => {
			const value = _params[key]
			if (key === 'sign' || [undefined, null, NaN].includes(value)) {
				return acc
			}

			acc.push(Array.isArray(value) || typeof value === 'object' ? JSON.stringify(value) : String(value))
			return acc
		}, [])
		params.sign = tool.crypto.MD5(values.join(',')).toString()
		return params
	}
}

export default tool
